#pragma once
#include "../common/net.hpp"
#include "../common/message.hpp"
#include <future>
#include <functional>

namespace RPC {
    namespace Client {
        class Requestor {
            public:
                using Ptr = std::shared_ptr<Requestor>;
                using RequestCallback = std::function<void(const BaseMessage::Ptr&)>;
                using AsyncResponse = std::future<BaseMessage::Ptr>;
                struct RequestDescribe {
                    using Ptr = std::shared_ptr<RequestDescribe>;
                    BaseMessage::Ptr request;
                    RpcType type;
                    std::promise<BaseMessage::Ptr> response;
                    RequestCallback callback;
                };
                void onResponse(const BaseConnection::Ptr &conn, BaseMessage::Ptr &msg){
                    std::string rid = msg->id();
                    RequestDescribe::Ptr rdp = getDescribe(rid);
                    if (rdp.get() == nullptr) {
                        ERROR_LOG("收到响应 - %s，但是未找到对应的请求描述！", rid.c_str());
                        return;
                    }
                    if (rdp->type == RpcType::RPC_ASYN) {
                        rdp->response.set_value(msg);
                    }else if (rdp->type == RpcType::RPC_CALLBACK){
                        if (rdp->callback) rdp->callback(msg);
                    }else {
                        ERROR_LOG("请求类型未知！！");
                    }
                    delDescribe(rid);
                }
                bool send(const BaseConnection::Ptr &conn, const BaseMessage::Ptr &req, AsyncResponse &async_rsp) {
                    RequestDescribe::Ptr rdp = newDescribe(req, RpcType::RPC_ASYN);
                    if (rdp.get() == nullptr) {
                        ERROR_LOG("构造请求描述对象失败！");
                        return false;
                    }
                    conn->send(req);
                    async_rsp = rdp->response.get_future();
                    return true;
                }
                bool send(const BaseConnection::Ptr &conn, const BaseMessage::Ptr &req, BaseMessage::Ptr &rsp) {
                    AsyncResponse rsp_future;
                    bool ret = send(conn, req, rsp_future);
                    if (ret == false) {
                        return false;
                    }
                    rsp = rsp_future.get();
                    return true;
                }
                bool send(const BaseConnection::Ptr &conn, const BaseMessage::Ptr &req, const RequestCallback &cb) {
                    RequestDescribe::Ptr rdp = newDescribe(req, RpcType::RPC_CALLBACK, cb);
                    if (rdp.get() == nullptr) {
                        ERROR_LOG("构造请求描述对象失败！");
                        return false;
                    }
                    conn->send(req);
                    return true;
                }
            private:
                RequestDescribe::Ptr newDescribe(const BaseMessage::Ptr &req, RpcType rpcType, 
                    const RequestCallback &cb = RequestCallback()) {
                    std::unique_lock<std::mutex> lock(_mutex);
                    RequestDescribe::Ptr rd = std::make_shared<RequestDescribe>();
                    rd->request = req;
                    rd->type = rpcType;
                    if (rpcType == RpcType::RPC_CALLBACK && cb) {
                        rd->callback = cb;
                    }
                    _request_desc.insert(std::make_pair(req->id(), rd));
                    return rd;
                }

                RequestDescribe::Ptr getDescribe(const std::string &rid) {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _request_desc.find(rid);
                    if (it == _request_desc.end()) {
                        return RequestDescribe::Ptr();
                    }
                    return it->second;
                }

                void delDescribe(const std::string &rid) {
                    std::unique_lock<std::mutex> lock(_mutex);
                    _request_desc.erase(rid);
                }
            private:
                std::mutex _mutex;
                std::unordered_map<std::string, RequestDescribe::Ptr> _request_desc;
        };

        class RequestorFactory
        {
        public:
            template<typename ...Args>
            static Requestor::Ptr create(Args ...args)
            {
                return std::make_shared<Requestor>(args...);
            }
        };
    }
}